package com.taichuan.selfcheck.ui.fragment;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.orhanobut.logger.Logger;
import com.taichuan.keysupport.Key;
import com.taichuan.selfcheck.R;
import com.taichuan.selfcheck.config.PropertyDefaultValue;
import com.taichuan.selfcheck.mvp.presenter.EmptyPresenter;
import com.taichuan.selfcheck.mvp.viewinterface.EmptyInterface;
import com.taichuan.selfcheck.ui.dialog.ResultDialog;
import com.taichuan.selfcheck.ui.fragment.base.AppBaseFragment;
import com.taichuan.tcutils.taichuan.SystemUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import butterknife.BindView;
import butterknife.OnClick;

/**
 * 该类主要用于
 *
 * @author CamelLuo
 * @version 2021/1/30
 */
public class BaseWebViewFragment extends AppBaseFragment<EmptyInterface, EmptyPresenter> implements EmptyInterface {
    private static final String TAG = BaseWebViewFragment.class.getSimpleName();

    /**
     * 页面超时时长
     */
    private static final int WEB_TIMEOUT = 20000;

    private static final String KEY_WEBVIEW_URL = "key_webView_utl";

    ResultDialog resultDialog;
    @BindView(R.id.webViewKeyTipTv)
    TextView keyTipTv;
    @BindView(R.id.base_webView)
    WebView webView;
    @BindView(R.id.web_progress)
    ProgressBar progressBar;
    @BindView(R.id.web_error_layout)
    RelativeLayout errorLayout;
    @BindView(R.id.webView_back_button)
    Button backBtn;
    @BindView(R.id.webView_refresh_button)
    Button refreshBtn;
    @BindView(R.id.webView_forward_button)
    Button forwardBtn;
    @BindView(R.id.webView_operate_layout)
    ConstraintLayout operateLayout;

    /**
     * 是否出现超时错误
     */
    private boolean isTimeoutError = false;
    /**
     * 是否出现页面连接错误
     */
    private boolean isNetError = false;

    /**
     * 打开WebViewFragment时所传入的Url地址
     */
    private String startUrl;
    /**
     * 当前准备加载页面的Url地址
     */
    private String shouldloadUrl;

    @Override
    protected int setContentViewId() {
        //解决无法在系统App中使用WebView的问题
        hookWebView();
        return R.layout.base_webview_fragment;
    }

    @Override
    protected void initView(View view, Bundle bundle) {
        resultDialog = new ResultDialog(PropertyDefaultValue.FUNCTION_TEST_21, getResources().getString(R.string.public_ip_query), getContext());       //设置弹窗发送的事件——公网ip查询
        if (SystemUtils.isTouchDevice()){
            operateLayout.setVisibility(View.VISIBLE);
            keyTipTv.setVisibility(View.GONE);
        }else {
            operateLayout.setVisibility(View.GONE);
            keyTipTv.setVisibility(View.VISIBLE);
        }

        //初始化WebView
        initWebView();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        destoryWebView();
    }

    /**
     * 定义一个方法用于让外部传入Bundle所需要的参数
     * @param webUrl 需加载的url网址
     * @return
     */
    public static Bundle setBundleValue(String webUrl){
        Bundle bundle = new Bundle();
        bundle.putString(KEY_WEBVIEW_URL,webUrl);
        return bundle;
    }

    @Override
    protected void getBundleValues(Bundle bundle) {
        if (bundle != null){
            startUrl = bundle.getString(KEY_WEBVIEW_URL,"");
        }
    }

    /**
     * 定义一个方法用于注销WebView
     */
    private void destoryWebView() {
        if (webView != null) {
            webView.stopLoading(); //停止加载
            ((ViewGroup) webView.getParent()).removeView(webView); //把webview从视图中移除
            webView.removeAllViews(); //移除webview上子view
            webView.clearCache(true); //清除缓存
            webView.clearHistory(); //清除历史
            webView.destroy(); //销毁WebView，防止内存泄漏
        }
    }

    private void initWebView() {
        //设置WebView
        initWebViewSetting();

        //设置WebView客户端
        webView.setWebViewClient(new MeWebViewClient());

        //设置Chrome客户端
        webView.setWebChromeClient(new MyWebViewChromeClient());

        //加载网页
        if (!TextUtils.isEmpty(startUrl)){
            Log.e(TAG, "initWebView: 执行加载的网址url为： " + startUrl);
            webView.loadUrl(startUrl);
        }
    }

    /**
     * 定义一个方法用于设置WebView
     */
    private void initWebViewSetting() {
        //声明WebSettings子类
        WebSettings webSettings = webView.getSettings();
        //如果访问的页面中要与Javascript交互，则webview必须设置支持Javascript
        webSettings.setJavaScriptEnabled(true);

        //设置自适应屏幕，两者合用
        webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小

        //缩放操作
//        webSettings.setSupportZoom(true); //支持缩放，默认为true。是下面那个的前提。
//        webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false，则该WebView不可缩放
//        webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件

        //其他细节操作
        webSettings.setAllowFileAccess(true); //设置可以访问文件
        webSettings.setAllowFileAccessFromFileURLs(true);
        webSettings.setAllowUniversalAccessFromFileURLs(true);

        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
        webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
        webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
        //设置缓存模式，使用默认缓存
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //支持HTML5 的 DOM Storage 机制存储 ***
        webSettings.setDomStorageEnabled(true);
        //设置App缓存路径
        webSettings.setAppCacheEnabled(true);
        webSettings.setAppCacheMaxSize(Long.MAX_VALUE);
        webSettings.setAppCachePath(getActivity().getDir("appcache", 0).getPath());

        //设置支持本地存储
        webSettings.setDatabaseEnabled(true);
        String dbPath = getActivity().getDir("database", Context.MODE_PRIVATE).getPath();
        webSettings.setDatabasePath(dbPath);

        //在网页加载完成后再加载图片
        webSettings.setLoadsImagesAutomatically(true);

        //调试WebView，需搭配Google Chrome浏览器的DevTools工具（chrome://inspect/#devices）
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WebView.setWebContentsDebuggingEnabled(true);
        }

    }

    private class MeWebViewClient extends WebViewClient {
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            Logger.t(TAG).e("网页加载 onPageStarted：  " + url);
            //显示进度条
            showLoading();

            //记录当前加载的url
            shouldloadUrl = url;
            //显示进度条
            progressBar.setVisibility(View.VISIBLE);
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                //启动超时线程
                timeoutHandler.postDelayed(timeoutRunnable, WEB_TIMEOUT);
            }
        }

        @Override
        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
            super.onReceivedError(view, request, error);
            //android 6.0 以上设备通过该方法实现页面加载失败，超时等处理
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                String requestUrl = request.getUrl().toString().trim();
                Logger.t(TAG).e("网页加载失败：  " + error.getDescription() + "  错误码为：" + error.getErrorCode() + "\n"
                        + "  加载的url为：**" + requestUrl + "当前webview url为:**" + webView.getUrl()
                        + "  shouldOverrideUrlLoading 中的url为：  " + shouldloadUrl);
                int errorCode = error.getErrorCode();

                //网页超时判断（只判断网页的主url，其内部请求超时不予响应）
                if (errorCode == ERROR_TIMEOUT) {
                    Logger.t(TAG).e("是否匹配：  " + shouldloadUrl.trim().equals(requestUrl));
                    if (shouldloadUrl.trim().equals(requestUrl)) {
                        //记录网页加载超时标志
                        isTimeoutError = true;
                        //显示错误页面并隐藏webview
                        errorLayout.setVisibility(View.VISIBLE);
                        webView.setVisibility(View.GONE);
                    }
                }
            }
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Logger.t(TAG).e("网页加载完成onPageFinished：  ");
            //隐藏加载框
            hideLoading();
            //andoroid6.0以下使用Handler作为超时机制 撤销超时计时
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                timeoutHandler.removeCallbacks(timeoutRunnable);
            }

        }

        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
            super.onReceivedHttpError(view, request, errorResponse);
            int statusCode = errorResponse.getStatusCode();
            Logger.t(TAG).e("页面加载失败，onReceivedHttpError code = " + statusCode + "request 为：" + request.getUrl());
            /**
             * 2018.4.12 暂时屏蔽
             */
            if (statusCode == 404 || statusCode == 500) {
                errorLayout.setVisibility(View.VISIBLE);
                webView.setVisibility(View.GONE);
            }
        }
    }

    /**
     * 定义一个内部类用于继承WebChromeClient
     */
    private class MyWebViewChromeClient extends WebChromeClient {

        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            Log.e("WebView", "网页加载进度：" + newProgress);
            if (newProgress == 100) {
                // 网页加载完成
                progressBar.setVisibility(View.GONE);
                //当页面没有出现超时错误和网络连接错误时允许reload的网页进行显示
                if (!isTimeoutError && !isNetError) {
                    webView.setVisibility(View.VISIBLE);
                    errorLayout.setVisibility(View.GONE);
                }
            } else {
                // 加载中
                progressBar.setProgress(newProgress);
            }
            super.onProgressChanged(view, newProgress);
        }

        @Override
        public void onReceivedTitle(WebView view, String title) {
            super.onReceivedTitle(view, title);
            // android 6.0 以下通过title获取
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                Logger.t(TAG).e("加载到的页面标题为：  " + title);
                if (title.contains("404") || title.contains("500") || title.contains("Error") || title.contains("找不到网页") || title.contains("网页无法打开")) {
                    //记录网页连接错误标志
                    isNetError = true;
                    errorLayout.setVisibility(View.VISIBLE);
                    webView.setVisibility(View.GONE);
                } else {
                    //andoroid6.0以下使用Handler作为超时机制 撤销超时计时
                    //为防止部分网页请求内部图片元素时间过长失败导致页面超时，因此在读取到页面标题时认为该页面能够显示，撤销超时计时
                    timeoutHandler.removeCallbacks(timeoutRunnable);
                }
            }
        }
    }

    private Handler timeoutHandler = new Handler();

    /**
     * 定义一个线程用于执行页面超时操作
     */
    private final Runnable timeoutRunnable = new Runnable() {

        @Override
        public void run() {
            webView.stopLoading();
            //记录网页加载失败标志
            isTimeoutError = true;
            errorLayout.setVisibility(View.VISIBLE);
            webView.setVisibility(View.GONE);
        }
    };

    /**
     * 定义一个方法用于解决 使用系统签名的App程序无法使用WebView的问题
     * 报错语句是：java.lang.UnsupportedOperationException: For security reasons, WebView is not allowed in privileged processes
     * 因难以避免WebView存在安全漏洞，系统遭受攻击，Android不允许特权进程应用使用WebView。如果使用了，便会抛出以上异常。
     * 特权进程包括sharedUserId为ROOT_UID和SYSTEM_UID的进程，从Android O（8.0）开始共享PHONE_UID、NFC_UID、BLUETOOTH_UID的进程也属于特权进程。
     * 如果不幸，你的应用共享了以上的UserId，又有使用WebView，将会Crash掉，并抛出异常
     */
    public static void hookWebView() {
        int sdkInt = Build.VERSION.SDK_INT;
        try {
            Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
            Field field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            Object sProviderInstance = field.get(null);
            if (sProviderInstance != null) {
                Log.i(TAG, "sProviderInstance isn't null");
                return;
            }

            Method getProviderClassMethod;
            if (sdkInt > 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (sdkInt == 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else {
                Log.i(TAG, "Don't need to Hook WebView");
                return;
            }
            getProviderClassMethod.setAccessible(true);
            Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor<?> delegateConstructor = delegateClass.getDeclaredConstructor();
            delegateConstructor.setAccessible(true);
            if (sdkInt < 26) {//低于Android O版本
                Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
                if (providerConstructor != null) {
                    providerConstructor.setAccessible(true);
                    sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
                }
            } else {
                Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
                chromiumMethodName.setAccessible(true);
                String chromiumMethodNameStr = (String) chromiumMethodName.get(null);
                if (chromiumMethodNameStr == null) {
                    chromiumMethodNameStr = "create";
                }
                Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
                if (staticFactory != null) {
                    sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
                }
            }

            if (sProviderInstance != null) {
                field.set("sProviderInstance", sProviderInstance);
                Log.i(TAG, "Hook success!");
            } else {
                Log.i(TAG, "Hook failed!");
            }
        } catch (Throwable e) {
            Log.w(TAG, e);
        }
    }

    @OnClick({R.id.webView_refresh_button,R.id.webView_forward_button,R.id.webView_back_button})
    @Override
    protected void onXmlClick(View v) {
        super.onXmlClick(v);
        switch (v.getId()){
            case R.id.webView_refresh_button:{          //刷新
                //用户点击重新加载，将超时错误记录消除
                isTimeoutError = false;
                isNetError = false;
                //显示加载失败页面
                errorLayout.setVisibility(View.GONE);
                //重新加载
                webView.reload();
                break;
            }
            case R.id.webView_forward_button:{         //前进
                Log.e(TAG, "onXmlClick: 执行前进： " + webView.canGoForward());
                if (webView.canGoForward()){
                    webView.goForward();
                }
                break;
            }
            case R.id.webView_back_button:{             //后退
                Log.e(TAG, "onXmlClick: 执行后退： " + webView.canGoBack());
                if (webView.canGoBack()){
                    webView.goBack();
                }
                break;
            }
        }
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (resultDialog.isShowing()) {
            resultDialog.dispatchKeyEvent(event);
        }
        if (event.getKeyCode() == Key.Convert.KEYCODE_5) {     //刷新页面
            refreshBtn.performClick();
        }
        return super.dispatchKeyEvent(event);
    }

    @Override
    protected boolean isAllowAutoFinishFragment() {
        return false;
    }

    @Override
    public void doOnAutoFinishFragment() {
        resultDialog.show();
    }

    @Override
    protected EmptyPresenter createPresenter() {
        return new EmptyPresenter();
    }
}
